package org.gaaidou.ptarmigan.springboot.core.log;


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.gaaidou.ptarmigan.springboot.core.common.*;
import org.gaaidou.ptarmigan.springboot.core.serialize.json.Desensitize;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Aspect
@Order(1)
@Component
@EnableConfigurationProperties(LogProperties.class)
public class LoggerAop {
    private static final Logger logger = LoggerFactory.getLogger(LoggerAop.class);

    @Autowired
    private LogProperties logProperties;


//    @Around("within(@org.springframework.stereotype.Controller *)")
    public Object log(ProceedingJoinPoint point) throws Throwable {
        Method method = ((MethodSignature) point.getSignature()).getMethod();
        String methodName = ClazzUtil.getMethodName(method);

        if (AnnotationUtil.isAnnotation(method, ExcludeLog.class)) {
            return point.proceed();
        }

        long start = System.currentTimeMillis();
        Object result = null;
        try {
            result = point.proceed();
            return result;
        } finally {
            List<String> argsList = IntStream.range(0, point.getArgs().length)
                    .mapToObj(index -> {
                        if (AnnotationUtil.isAnnotation(method, index, ExcludeLog.class)) {
                            return Common.EMPTY_STRING;
                        } else if (AnnotationUtil.isAnnotation(method, index, Desensitize.class)) {
                            return Common.ASTERISK_STRING;
                        } else {
                            return Objects.toString(point.getArgs()[index]);
                        }
                    }).collect(Collectors.toList());
            String args = String.join(",", argsList);
            log(methodName, start, result, args, System.currentTimeMillis());
        }
    }


    private void log(String methodName, long start, Object result, String args, long end) {
        String resultStr = Objects.toString(result);
        switch (logProperties.getLevel()) {
            case INFO -> logger.info(logProperties.getFormat(), methodName, (end - start), args, resultStr);
            case WARNING -> logger.warn(logProperties.getFormat(), methodName, (end - start), args, resultStr);
            case ERROR -> logger.error(logProperties.getFormat(), methodName, (end - start), args, resultStr);
            default -> logger.debug(logProperties.getFormat(), methodName, (end - start), args, resultStr);
        }
    }

}
